package com.le.tester.webpage.secKill.service.impl;

import com.le.tester.common.Const;
import com.le.tester.common.enums.RedisKey;
import com.le.tester.dao.GoodsMapper;
import com.le.tester.dao.OrderInfoMapper;
import com.le.tester.dao.SecKillGoodsMapper;
import com.le.tester.dao.SecKillOrderMapper;
import com.le.tester.dto.GoodsDto;
import com.le.tester.dto.OrderDetailDto;
import com.le.tester.entity.Goods;
import com.le.tester.entity.OrderInfo;
import com.le.tester.entity.SecKillGoods;
import com.le.tester.entity.SecKillOrder;
import com.le.tester.handler.BusinessException;
import com.le.tester.webpage.secKill.service.OrderService;
import com.le.tester.webpage.secKill.service.SecKillService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 描述：秒杀的接口实现
 */
@Service
public class SecKillServiceImpl implements SecKillService {

    private static final Logger LOG = LoggerFactory.getLogger(SecKillServiceImpl.class);

    @Autowired
    private OrderService orderService;

    @Resource
    private GoodsMapper goodsMapper;

    @Resource
    private SecKillGoodsMapper secKillGoodsMapper;

    @Resource
    private SecKillOrderMapper secKillOrderMapper;

    @Resource
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public OrderDetailDto getSecKillOrderDetail(Long orderId) {
        //根据订单号码获取到订单详情
        OrderInfo orderDetail = orderService.getOrderDetail(orderId);

        //获取到秒杀的订单详情
        GoodsDto goodsDto = this.getSecKillOrderDetailByGoodsId(orderDetail.getGoodsId());

        OrderDetailDto orderDetailDto = new OrderDetailDto();
        orderDetailDto.setOrderInfo(orderDetail);
        orderDetailDto.setGoodsDto(goodsDto);
        return orderDetailDto;
    }

    /**
     * 进行秒杀
     *
     * @param goodsId ：商品id
     * @return
     */
    @Override
    public Map<String, Object> doSecKill(Long goodsId) {
        //根据商品id获取秒杀商品的信息
        GoodsDto goodsDto = goodsMapper.selectSecDetailByGoods(goodsId);
        Map<String, Object> resultMap = new HashMap<>();
        //判断是不是已经秒杀过了(伪代码，userId)
        AtomicLong atomicLong = new AtomicLong(1410080408);
        long userId = atomicLong.incrementAndGet();
        LOG.info("secKillOrderUserId:{}", userId);
        //如果你要每个用户只能抢一次的话，这个判断就应该具有原子的,先加个重锁
//        SecKillOrder secKillOrder = secKillOrderMapper.selectSecKillOrderByUserIdGoodsId(userId, goodsId);
//        if (secKillOrder != null) {
//            resultMap.put("result", "secKill fail secKillOrder is not null");
//            return resultMap;
//        }
        //保存信息入库
        //ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        //Lock writeLock = lock.writeLock();
        //writeLock.lock();
        OrderInfo orderInfo = this.saveOrderInfo(userId, goodsDto);
        resultMap.put("orderInfo", orderInfo);
        resultMap.put("goods", goodsDto);
        return resultMap;

    }

    //直接加上两个重的锁，吞吐量直接降下来一半,synchronized实在太重了，一分钟少了一千5左右的请求
    private synchronized OrderInfo saveOrderInfo(long userId, GoodsDto goodsDto) {
        //ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(true);
        //ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        //AtomicBoolean limitNum = new AtomicBoolean(booleanMap.get("limitNum"));
        //AtomicBoolean reduceStock = new AtomicBoolean(booleanMap.get("reduceStock"));
        //进行减库存(使用了redisson的分布式锁，并没有出现超卖的现象)
        //ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(true);
        //拿到一个写入的锁
        //Lock writeLock = reentrantReadWriteLock.writeLock();
        //if (null != booleanMap.get("limitNum") && (!booleanMap.get("limitNum"))) {
        //writeLock.lock();
        //try {
        Map<String, Boolean> booleanMap = this.limitNumAndReduceStock(userId, goodsDto);
        ;
        if (!booleanMap.get("limitNum")) {
            this.setLimitNum(goodsDto.getId());
            return null;
        }
//        if (booleanMap.get("reduceStock")) {
        if (booleanMap.get("reduceStock")) {
            //订单信息
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setAddrId(1L);
            orderInfo.setGoodsId(goodsDto.getId());
            orderInfo.setCreateDate(new Date());
            orderInfo.setGoodsCount(1);
            orderInfo.setOrderChannel(1);
            orderInfo.setGoodsName(goodsDto.getGoodsName());
            orderInfo.setStatus(0);
            orderInfo.setGoodsPrice(goodsDto.getSecKillPrice());
            orderInfo.setUserId((long) userId);
            orderInfoMapper.insertSelective(orderInfo);
            //新增到秒杀订单
            SecKillOrder secKillOrder = new SecKillOrder();
            secKillOrder.setUserId(userId);
            secKillOrder.setGoodsId(goodsDto.getId());
            secKillOrder.setOrderId(orderInfo.getId());
            secKillOrderMapper.insertSelective(secKillOrder);
            return orderInfo;
        } else {
            setSecKillOver(goodsDto.getId());
            return null;
        }
    }
    //finally {
    //writeLock.unlock();
    //}

    //全部抢购完了
    private void setSecKillOver(Long id) {
        throw new BusinessException("商品被抢购空了,商品id:" + id);
    }

    //限购次数了
    private void setLimitNum(Long id) {
        throw new BusinessException("次数限制了,商品id:" + id);
    }

    private Map<String, Boolean> limitNumAndReduceStock(long userId, GoodsDto goodsDto) {
        String secondKillKey = RedisKey.DEC_STORE_LOCK.getCode() + Const.CONNECT_STR + goodsDto.getId();
        RLock lock = redissonClient.getLock(secondKillKey);
        Map<String, Boolean> resultMap = new HashMap<>();
        //减去总的库存
        try {
            //一定要先去查一次库存
            lock.lock();
            //每个用户只能抢购一个
            SecKillOrder secKillOrder = secKillOrderMapper.selectSecKillOrderByUserIdGoodsId(userId, goodsDto.getId());
            if (null != secKillOrder) {
                resultMap.put("limitNum", false);
                return resultMap;
            } else {
                resultMap.put("limitNum", true);
            }
            Goods goods = goodsMapper.selectByPrimaryKey(goodsDto.getId());
            AtomicInteger atomicInteger = new AtomicInteger(goods.getGoodsStock());
            if (atomicInteger.get() <= 0) {
                resultMap.put("reduceStock", false);
                return resultMap;
            }
            goods.setGoodsStock(atomicInteger.decrementAndGet());
            goodsMapper.updateByPrimaryKey(goods);

            //另一个库存(根据商品id获取)
            SecKillGoods secKillGoods = secKillGoodsMapper.selectSecKillInfoByGoodsId(goodsDto.getId());
            //SecKillGoods secKillGoods = secKillGoodsMapper.selectByPrimaryKey(goodsDto.getId());
            //获取库存
            AtomicInteger secKillOrderStock = new AtomicInteger(secKillGoods.getStockCount());
            if (secKillOrderStock.get() <= 0) {
                resultMap.put("reduceStock", false);
                return resultMap;
            }
            secKillGoods.setStockCount(secKillOrderStock.decrementAndGet());
            secKillGoodsMapper.updateByPrimaryKey(secKillGoods);
            resultMap.put("reduceStock", true);
            return resultMap;
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        } finally {
            lock.unlock();
        }
    }

    private SecKillOrder getSecKillOrderByUserIdGoodsId(long userId, Long goodsId) {
        //不能进行重复的秒杀
        return secKillOrderMapper.selectSecKillOrderByUserIdGoodsId(userId, goodsId);
    }

    private GoodsDto getSecKillOrderDetailByGoodsId(Long goodsId) {

        //根据商品id获取商品详情
        return goodsMapper.selectSecDetailByGoods(goodsId);
    }
}
